#ifndef __tenacitas_return_code_core__rc__h__
#define __tenacitas_return_code_core__rc__h__


/** @todo */

// ==> C++ Headers
#include <cstdint>
#include <iostream>
#include <string>
#include <sstream>
#include <limits>

// ==> 3rds Headers

// ==> Our Headers

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace return_code {
        /** ==> application, bussiness, display, interaction, process, storage,
            transmittion */
        namespace core {
            // ==> Typedefs

            /** */
            typedef std::uint64_t class_id;

            /** */
            typedef std::uint16_t constant_id;

            /** Base class */
            template <typename type = void *>
            class rc;


            // ==> Attributes 

            // ==> Functions

            // ==> Classes
            /** Allows a return code to have a flag to indicate success or
                error. 
                In case of success, it can contain an object returned; and in
                case of failure, it contains information about the error.

                @tparam type is the type of object returned in case of success
             */
            template <typename type>
            class rc : public std::exception {
            public:

                // ==> rc - Friends

                /** Class allowed to access private members of @p rc, in
                    order to perform tests */
                friend class rc_tester;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const rc & p_rc) {
                    if (p_rc.m_success) {
                        p_out << "success: (" << p_rc.m_obj << ")";
                    }
                    else {
                        p_out << "failure: ("
                              << p_rc.m_class << ", "
                              << p_rc.m_cte << ", "
                              << p_rc.m_file << ", "
                              << p_rc.m_line << ")";
                    }
                    return p_out;
                }
                

                // ==> rc - Public Constructors

                /** Default return code, hoping for the best */
                inline rc() noexcept
                    : m_success(true),
                      m_obj(),
                      m_class(0),
                      m_cte(0),
                      m_file(""),
                      m_line(std::numeric_limits<int64_t>::min()),
                      m_desc(""){}


                /** Creating a return code in case of an error */
                inline rc(const class_id & p_class,
                          const constant_id & p_cte,
                          const char * p_file,
                          int64_t p_line,
                          const char * p_desc = "") noexcept 
                    : m_success(false),
                      m_obj(),
                      m_class(p_class),
                      m_cte(p_cte),
                      m_file(p_file),
                      m_line(p_line),
                      m_desc(p_desc){}

                /** Creating a return code in case of success */
                inline rc(type && p_obj) noexcept
                    : m_success(true),
                      m_obj(std::move(p_obj)),
                      m_class(0),
                      m_cte(0),
                      m_file(""),
                      m_line(std::numeric_limits<int64_t>::min()),
                      m_desc(""){}

                /** */
                rc(const rc &) = delete;

                /** */
                inline rc(rc && p_rc) noexcept {
                    m_success = std::move(p_rc.m_success);
                    m_obj = std::move(p_rc.m_obj);
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                }

                /** */
                template <typename other>
                inline rc(const rc<other> & p_rc)
                    : m_success(p_rc),
                      m_obj(),
                      m_class(p_rc.get_class_id()),
                      m_cte(p_rc.get_constant_id()),
                      m_file(p_rc.get_file()),
                      m_line(p_rc.get_line()),
                      m_desc(p_rc.get_desc()){}
                

                /** Destructor */
                inline ~rc() {}

                // ==> rc - Public Accessors

                /** */
                inline operator bool() const noexcept {
                    return m_success;
                }

                /** */
                inline class_id get_class_id() const noexcept {
                    return m_class;
                }

                /** */
                inline constant_id get_constant_id() const noexcept {
                    return m_cte;
                }

                /** */
                inline type && get_obj() noexcept {
                    return std::move(m_obj);
                }
                
                inline const std::string & get_file() const noexcept {
                    return m_file;
                }

                inline int64_t get_line() const noexcept {
                    return m_line;
                }

                inline const std::string & get_desc() const noexcept {
                    return m_desc;
                }

                /** */
                const char* what() const noexcept {
                    static std::stringstream _ss;
                    try {
                        _ss.clear();
                        _ss << "(" << m_class << ","
                            << m_cte << ", "
                            << m_file << ","
                            << m_line;
                    
                        if (!m_desc.empty()) {
                            _ss << ", " << m_desc ;
                        }
                    
                        _ss << ")";

                    } catch (const std::exception & _ex) {
                        static const char * _str = "ERROR";
                        return _str;                        
                    }
                    return _ss.str().c_str();
                }
                
                
                // ==> rc - Public Operators

                /** */
                rc & operator=(const rc &)=delete;

                /** */
                inline rc & operator=(rc && p_rc) noexcept {
                    m_success = std::move(p_rc.m_success);
                    m_obj = std::move(p_rc.m_obj);
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                    return *this;
                }

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                /** not allowed */
                bool operator ==(const rc & )=delete;

                /** not allowed */
                bool operator !=(const rc & )=delete;

                /** not allowed */
                bool operator >=(const rc & )=delete;

                /** not allowed */
                bool operator >(const rc & )=delete;

                /** not allowed */
                bool operator <=(const rc & )=delete;

                /** not allowed */
                bool operator <(const rc & )=delete;

            private:

                // ==> rc - Private Attributes

                /** */
                bool m_success;

                /** */
                type m_obj;

                /** */
                class_id m_class;

                /** */
                constant_id m_cte;

                /** */
                std::string m_file;

                /** */
                int64_t m_line;

                /** */
                std::string m_desc;
            };

            /** Allows a return code to have a flag to indicate success or
                error. 
                In case of success, it can contain an object returned; and in
                case of failure, it contains information about the error.

                @tparam type * is the type of pointer to object returned in case
                of success 
             */
            template <typename type>
            class rc<type *> : public std::exception {
            public:

                // ==> rc - Friends

                /** Class allowed to access private members of @p rc, in
                    order to perform tests */
                friend class rc_tester;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const rc & p_rc) {
                    if (p_rc.m_success) {
                        p_out << "success: (" << *p_rc.m_obj << ")";
                    }
                    else {
                        p_out << "failure: ("
                              << p_rc.m_class << ", "
                              << p_rc.m_cte << ", "
                              << p_rc.m_file << ", "
                              << p_rc.m_line << ")";
                    }
                    return p_out;
                }
                

                // ==> rc - Public Constructors

                /** Default return code, hoping for the best */
                inline rc() noexcept
                    : m_success(true),
                      m_obj(nullptr),
                      m_class(0),
                      m_cte(0),
                      m_file(""),
                      m_line(std::numeric_limits<int64_t>::min()),
                      m_desc(""){}


                /** Creating a return code in case of an error */
                inline rc(class_id && p_class,
                          constant_id p_cte,
                          const char * p_file,
                          int64_t p_line,
                          const char * p_desc = "") noexcept 
                    : m_success(false),
                      m_obj(nullptr),
                      m_class(p_class),
                      m_cte(p_cte),
                      m_file(p_file),
                      m_line(p_line),
                      m_desc(p_desc){}

                /** Creating a return code in case of success */
                inline rc(type ** p_obj) noexcept
                    : m_success(true),
                      m_obj(nullptr),
                      m_class(0),
                      m_cte(0),
                      m_file(""),
                      m_line(std::numeric_limits<int64_t>::min()),
                      m_desc(""){
                    if (*p_obj) {
                        m_obj = std::move(*p_obj);
                        *p_obj = nullptr;
                    }                    
                }

                /** */
                rc(const rc &) = delete;

                /** */
                inline rc(rc && p_rc) noexcept
                    : rc() {
                    m_success = std::move(p_rc.m_success);
                    if (p_rc.m_obj) {
                        m_obj = std::move(p_rc.m_obj);
                        p_rc.m_obj = nullptr;
                    }
                    
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                }

                /** */
                template <typename other>
                inline rc(const rc<other> & p_rc)
                    : m_success(p_rc),
                      m_obj(),
                      m_class(p_rc.get_class_id()),
                      m_cte(p_rc.get_constant_id()),
                      m_file(p_rc.get_file()),
                      m_line(p_rc.get_line()),
                      m_desc(p_rc.get_desc()){}
                

                /** Destructor */
                inline ~rc() {
                    if (m_obj) {
                        delete m_obj;
                        m_obj = nullptr;
                    }
                }

                // ==> rc - Public Accessors

                /** */
                inline operator bool() const noexcept {
                    return m_success;
                }

                /** */
                inline class_id get_class_id() const noexcept {
                    return m_class;
                }

                /** */
                inline constant_id get_constant_id() const noexcept {
                    return m_cte;
                }

                /** */
                inline type * && get_obj() noexcept {
                    return std::move(m_obj);
                }
                
                inline const std::string & get_file() const noexcept {
                    return m_file;
                }

                inline int64_t get_line() const noexcept {
                    return m_line;
                }

                inline const std::string & get_desc() const noexcept {
                    return m_desc;
                }

                /** */
                const char* what() const noexcept {
                    static std::stringstream _ss;
                    try {
                        _ss.clear();
                        _ss << "(" << m_class << ","
                            << m_cte << ", "
                            << m_file << ","
                            << m_line;
                    
                        if (!m_desc.empty()) {
                            _ss << ", " << m_desc ;
                        }
                    
                        _ss << ")";

                    } catch (const std::exception & _ex) {
                        static const char * _str = "ERROR";
                        return _str;                        
                    }
                    return _ss.str().c_str();
                }
                
                
                // ==> rc - Public Operators

                /** */
                rc & operator=(const rc &)=delete;

                /** */
                inline rc & operator=(rc && p_rc) noexcept {
                    m_success = std::move(p_rc.m_success);
                    if (p_rc.m_obj) {
                        if (m_obj) {
                            delete m_obj;
                            m_obj = nullptr;
                        }
                        m_obj = std::move(p_rc.m_obj);
                        p_rc.m_obj = nullptr;
                    }
                    
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                    return *this;
                }

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                /** not allowed */
                bool operator ==(const rc & )=delete;

                /** not allowed */
                bool operator !=(const rc & )=delete;

                /** not allowed */
                bool operator >=(const rc & )=delete;

                /** not allowed */
                bool operator >(const rc & )=delete;

                /** not allowed */
                bool operator <=(const rc & )=delete;

                /** not allowed */
                bool operator <(const rc & )=delete;

            private:

                // ==> rc - Private Attributes

                /** */
                bool m_success;

                /** */
                type * m_obj;

                /** */
                class_id m_class;

                /** */
                constant_id m_cte;

                /** */
                std::string m_file;

                /** */
                int64_t m_line;

                /** */
                std::string m_desc;
            };


            /** Allows a return code to have a flag to indicate success or
                error. 
                In case of success, it can contain an object returned; and in
                case of failure, it contains information about the error.

                @tparam type * is the type of pointer to object returned in case
                of success 
             */
            template <>
            class rc<void *> : public std::exception {
            public:

                // ==> rc - Friends

                /** Class allowed to access private members of @p rc, in
                    order to perform tests */
                friend class rc_tester;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const rc & p_rc) {
                    if (p_rc.m_success) {
                        p_out << "success";
                    }
                    else {
                        p_out << "failure: ("
                              << p_rc.m_class << ", "
                              << p_rc.m_cte << ", "
                              << p_rc.m_file << ", "
                              << p_rc.m_line << ")";
                    }
                    return p_out;
                }
                

                // ==> rc - Public Constructors

                /** Default return code, hoping for the best */
                inline rc() noexcept
                    : m_success(true),
                      m_class(0),
                      m_cte(0),
                      m_file(""),
                      m_line(std::numeric_limits<int64_t>::min()),
                      m_desc(""){}


                /** Creating a return code in case of an error */
                inline rc(const class_id & p_class,
                          const constant_id & p_cte,
                          const char * p_file,
                          int64_t p_line,
                          const char * p_desc = "") noexcept 
                    : m_success(false),
                      m_class(p_class),
                      m_cte(p_cte),
                      m_file(p_file),
                      m_line(p_line),
                      m_desc(p_desc){}


                /** */
                rc(const rc &) = delete;

                /** */
                inline rc(rc && p_rc) noexcept {
                    m_success = std::move(p_rc.m_success);
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                }

                /** */
                template <typename other>
                inline rc(const rc<other> & p_rc)
                    : m_success(p_rc),
                      m_class(p_rc.get_class_id()),
                      m_cte(p_rc.get_constant_id()),
                      m_file(p_rc.get_file()),
                      m_line(p_rc.get_line()),
                      m_desc(p_rc.get_desc()){}

                /** Destructor */
                inline ~rc() {}

                // ==> rc - Public Accessors

                /** */
                inline operator bool() const noexcept {
                    return m_success;
                }

                /** */
                inline class_id get_class_id() const noexcept {
                    return m_class;
                }

                /** */
                inline constant_id get_constant_id() const noexcept {
                    return m_cte;
                }
                
                inline const std::string & get_file() const noexcept {
                    return m_file;
                }

                inline int64_t get_line() const noexcept {
                    return m_line;
                }

                inline const std::string & get_desc() const noexcept {
                    return m_desc;
                }

                /** */
                const char* what() const noexcept {
                    static std::stringstream _ss;
                    try {
                        _ss.clear();
                        _ss << "(" << m_class << ","
                            << m_cte << ", "
                            << m_file << ","
                            << m_line;
                    
                        if (!m_desc.empty()) {
                            _ss << ", " << m_desc ;
                        }
                    
                        _ss << ")";

                    } catch (const std::exception & _ex) {
                        static const char * _str = "ERROR";
                        return _str;                        
                    }
                    return _ss.str().c_str();
                }
                
                
                // ==> rc - Public Operators

                /** */
                rc & operator=(const rc &)=delete;

                /** */
                inline rc & operator=(rc && p_rc) noexcept {
                    m_success = std::move(p_rc.m_success);
                    m_class = std::move(p_rc.m_class);
                    m_cte = std::move(p_rc.m_cte);
                    m_file = std::move(p_rc.m_file);
                    m_line = std::move(p_rc.m_line);
                    m_desc = std::move(p_rc.m_desc);
                    return *this;
                }

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                /** not allowed */
                bool operator ==(const rc & )=delete;

                /** not allowed */
                bool operator !=(const rc & )=delete;

                /** not allowed */
                bool operator >=(const rc & )=delete;

                /** not allowed */
                bool operator >(const rc & )=delete;

                /** not allowed */
                bool operator <=(const rc & )=delete;

                /** not allowed */
                bool operator <(const rc & )=delete;

            private:

                // ==> rc - Private Attributes

                /** */
                bool m_success;

                /** */
                class_id m_class;

                /** */
                constant_id m_cte;

                /** */
                std::string m_file;

                /** */
                int64_t m_line;

                /** */
                std::string m_desc;
            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::return_code::core;


#endif
